﻿//-----------------------------------------------------------------------
// <copyright file="RequestResponseLoggingMiddleware.cs" company="SLWY Enterprises">
// * Copyright (C) 2017 四川商旅无忧科技有限公司 版权所有。
// * version : 1.0
// * author  : qinchaoyue
// * FileName: RequestResponseLoggingMiddleware.cs
// * history : created by qinchaoyue 2017-11-27 05:34:28
// </copyright>
//-----------------------------------------------------------------------

using System;
using System.IO;
using System.Text;
using System.Threading.Tasks;

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Internal;
using Slwy.FrameCore.Dependency;
using Slwy.Mvc.Context;

namespace Slwy.Mvc.Logging
{
    using Slwy.Context;
    using Slwy.Logger.Models;
    using Slwy.Utility.Extensions;

    using ILogger = Slwy.Logger.Contracts.ILogger;

    /// <summary>
    /// Acc 日志记录
    /// </summary>
    public class RequestResponseLoggingMiddleware
    {
        /// <summary>
        /// 组件
        /// </summary>
        private readonly RequestDelegate _next;

        /// <summary>
        /// 日志记录器
        /// </summary>
        private readonly ILogger _logger;

        /// <summary>
        /// Initializes a new instance of the <see cref="RequestResponseLoggingMiddleware"/> class. 
        /// 构造
        /// </summary>
        /// <param name="next">
        /// next
        /// </param>
        /// <param name="loggerFactory">
        /// log factory
        /// </param>
        public RequestResponseLoggingMiddleware(RequestDelegate next, ILogger loggerFactory)
        {
            this._next = next;
            this._logger = loggerFactory;
        }

        /// <summary>
        /// 日志记录器
        /// </summary>
        /// <param name="context"> The context.  </param>
        /// <returns> The <see cref="Task"/>.  </returns>
        public async Task Invoke(HttpContext context)
        {
            var injectedRequestStream = new MemoryStream();
            try
            {
                var requestTime = DateTime.Now;
                this.GetTraceId(context.Request);
                this.GetSequenceCode(context.Request);
                this.GetMethod(context, injectedRequestStream);
                this._logger.Acc(new AccModel { Direction = "Request", Interval = 0 });

                var originalBodyStream = context.Response.Body;

                using (var responseBody = new MemoryStream())
                {
                    context.Response.Body = responseBody;
                    await this._next(context);

                    this._logger.Acc(await this.FormatResponse(context.Response, requestTime));
                    await responseBody.CopyToAsync(originalBodyStream);
                }

            }
            catch (Exception e)
            {
                throw new Exception("this is a framecode bug", e);
            }
            finally
            {
                injectedRequestStream.Dispose();
            }
        }

        /// <summary>
        /// 获取 响应日志数据
        /// </summary>
        /// <param name="response"> The response.  </param>
        /// <param name="requestTime"> The request time.  </param>
        /// <returns> The <see cref="Task"/>.  </returns>
        private async Task<AccModel> FormatResponse(HttpResponse response, DateTime requestTime)
        {
            response.Body.Seek(0, SeekOrigin.Begin);
            var text = await new StreamReader(response.Body).ReadToEndAsync();
            response.Body.Seek(0, SeekOrigin.Begin);
            return new AccModel
            {
                Direction = "Response",
                Interval = requestTime.GetDifference(),
                OutResult = text,
            };
        }

        /// <summary>
        /// 获取请求入参和方法
        /// </summary>
        /// <param name="context">
        /// The context.
        /// </param>
        /// <param name="injectedRequestStream">
        /// The injected Request Stream.
        /// </param>
        private void GetMethod(HttpContext context, MemoryStream injectedRequestStream)
        {
            if (context.Request.Method.ToUpper() == "GET")
            {
                ContextManager.SetThisValue("InParam", context.Request.QueryString);
            }
            if (context.Request.Method.ToUpper() == "POST")
            {
                using (var bodyReader = new StreamReader(context.Request.Body))
                {
                    if (context.Request.ContentType?.IndexOf("multipart/form-data", StringComparison.Ordinal) != -1)
                    {
                        ContextManager.SetThisValue("InParam", "上传文件");
                    }
                    else
                    {
                        var bodyAsText = bodyReader.ReadToEnd();

                        var bytesToWrite = Encoding.UTF8.GetBytes(bodyAsText);
                        injectedRequestStream.Write(bytesToWrite, 0, bytesToWrite.Length);
                        injectedRequestStream.Seek(0, SeekOrigin.Begin);
                        context.Request.Body = injectedRequestStream;

                        ContextManager.SetThisValue("InParam", bodyAsText);
                    }
                }
                ContextManager.SetThisValue("Method", context.Request.Path);
                ContextManager.SetThisValue("ServiceUrl", context.Request.Host);
            }
        }

        /// <summary>
        /// 获取请求中的 traceId
        /// </summary>
        /// <param name="request">
        /// 请求
        /// </param>
        private void GetTraceId(HttpRequest request)
        {
            var traceId = request.Headers["TraceId"].ToString();
            if (traceId.IsNullOrEmpty())
            {
                traceId = Guid.NewGuid().ToString();
            }

            ContextManager.SetThisValue("TraceId", traceId);
        }

        /// <summary>
        /// 获取顺序码
        /// </summary>
        /// <param name="request">
        /// The request.  
        /// </param>
        private void GetSequenceCode(HttpRequest request)
        {
            var sequenceCode = request.Headers["SequenceCode"].ToString();
            if (sequenceCode.IsNullOrEmpty())
            {
                sequenceCode = "1";
            }
            else
            {
                // 1 = 1,  1-2 = 2, 1-2-3 = 3
                var count = sequenceCode.Split('-').Length;
                sequenceCode += $"-{count + 1}";
            }

            ContextManager.SetThisValue("SequenceCode", sequenceCode);
        }
    }

    /// <summary>
    /// 注册 acc 日志记录器
    /// </summary>
    public static class RequestResponseLoggingMiddlewareExtensions
    {
        /// <summary>
        /// 注册日志记录服务
        /// </summary>
        /// <param name="builder">
        /// The builder.
        /// </param>
        /// <returns>
        /// The <see cref="IApplicationBuilder"/>.
        /// </returns>
        public static IApplicationBuilder UseRequestResponseLogging(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<RequestResponseLoggingMiddleware>();
        }
    }
}
